<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2022/6/29
 * Time: 16:43
 */

namespace app\common\lib;


use FFMpeg\Coordinate\TimeCode;
use Illuminate\Support\Facades\Log;

class Ffmpeg
{
    protected $config = [];
    protected $ffmpeg = [];
    protected static $instance;

    public function __construct()
    {
        $this->config = config('param.ffmpeg');

        //ffmpeg插件路径，1步下载下来存放的路径
        $config = [
            'ffmpeg.binaries'  => $this->config['ffmpeg']??'ffmpeg.exe',   //'D:\ffmpeg\bin\ffmpeg.exe',
            'ffprobe.binaries' => $this->config['ffprobe']??'ffprobe.exe', //'D:\ffmpeg\bin\ffprobe.exe'
            'timeout'          => 3600, // The timeout for the underlying process
            'ffmpeg.threads'   => 12,   // The number of threads that FFMpeg should use
        ];
        $this->ffmpeg = \FFMpeg\FFMpeg::create($config);
    }

    /**
     * 保存视频第一帧作为图片
     * @param $saveFile
     * @param $videoFile
     * @param $quantity
     * @return \FFMpeg\Media\Frame
     */
    public function getFirstVideoToImage($saveFile, $videoFile, $quantity = 0.0001)
    {
        $video = $this->ffmpeg->open($videoFile);

        return $video->frame(TimeCode::fromSeconds($quantity))->save($saveFile);
    }

    /**
     * 获取视频文件的信息
     * @param $videoFilePath --文件路径（ffmpeg5.1版本可以用远程链接或者本地文件路径,4.85只能传本地文件路径）
     */
    public function getVideoInfo($videoFilePath)
    {
        try {
            ob_start();
            passthru(sprintf(config('param.ffmpeg')['ffmpeg'] . ' -i "%s" 2>&1', $videoFilePath));
            $videoInfo = ob_get_contents();
            ob_end_clean();

            $ret = [];
            // Duration: 00:33:42.64, start: 0.000000, bitrate: 152 kb/s
            if (preg_match("/Duration: (.*?), start: (.*?), bitrate: (\d*) kb\/s/", $videoInfo, $matches)) {
                $ret['duration'] = $matches[1]; // 视频长度
                $duration = explode(':', $matches[1]);
                $ret['seconds'] = $duration[0] * 3600 + $duration[1] * 60 + $duration[2]; // 转为秒数
                $ret['start'] = $matches[2]; // 开始时间
                $ret['bitrate'] = $matches[3]; // bitrate 码率 单位kb
            }
            // Stream #0:1: Video: rv20 (RV20 / 0x30325652), yuv420p, 352x288, 117 kb/s, 15 fps, 15 tbr, 1k tbn, 1k tbc
            if (preg_match("/Video: (.*?), (.*?), (.*?)[,\s]/", $videoInfo, $matches)) {
                $ret['vcodec'] = $matches[1];  // 编码格式
                $ret['vformat'] = $matches[2]; // 视频格式
                $ret['resolution'] = $matches[3]; // 分辨率
                list($width, $height) = explode('x', $matches[3]);
                $ret['width'] = $width;
                $ret['height'] = $height;
            }
            // Stream #0:0: Audio: cook (cook / 0x6B6F6F63), 22050 Hz, stereo, fltp, 32 kb/s
            if (preg_match("/Audio: (.*), (\d*) Hz/", $videoInfo, $matches)) {
                $ret['acodec'] = $matches[1];  // 音频编码
                $ret['asamplerate'] = $matches[2]; // 音频采样频率
            }
            if (isset($ret['seconds']) && isset($ret['start'])) {
                $ret['play_time'] = $ret['seconds'] + $ret['start']; // 实际播放时间
            }
            return $ret;
        } catch (\Exception $e) {
            Log::info('获取视频文件的信息失败-' . $e->getMessage());
            return false;
        }
    }

    /**
     * 转码
     * @param $videoFile
     */
    public function transcoding($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $format = new \FFMpeg\Format\Video\X264();
        $format->on('progress', function ($video, $format, $percentage) {
            echo "$percentage % transcoded";
        });

        $format->setKiloBitrate(1000)->setAudioChannels(2)->setAudioKiloBitrate(256);

        $video->save($format, storage_path('video.avi'));
    }

    /**
     * 提取图像
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function extractingImage($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        //提取一个图像，根据第几秒
//        $frame = $video->frame(\FFMpeg\Coordinate\TimeCode::fromSeconds(0.0001));
//        $frame->save(storage_path('image'.uniqid().'.jpg'));

        //提取多张图像
        $folder = storage_path('ffmpeg');
        $folder2 = storage_path('ffmpeg2');

//        $frameRate = \FFMpeg\Filters\Video\ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC;
//        $filter = new \FFMpeg\Filters\Video\ExtractMultipleFramesFilter($frameRate, $folder);
//        $frameFileType = 'jpg'; // either 'jpg', 'jpeg' or 'png'
//        $filter->setFrameFileType($frameFileType);
//        $video->addFilter($filter);

        $video->filters()
              ->extractMultipleFrames(\FFMpeg\Filters\Video\ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, $folder2)
              ->synchronize();

        $video->save(new \FFMpeg\Format\Video\X264(),'');
    }

    /**
     * 裁剪视频
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function clipVideo($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $clip = $video->clip(\FFMpeg\Coordinate\TimeCode::fromSeconds(30), \FFMpeg\Coordinate\TimeCode::fromSeconds(15));
        $clip->filters()->resize(new \FFMpeg\Coordinate\Dimension(720, 540), \FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_INSET, true);
        $clip->save(new \FFMpeg\Format\Video\X264(), storage_path('clipVideo3.avi'));
    }

    /**
     * 生成音频文件的波形
     * he output file MUST use the PNG extension.
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function generateWaveform($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        // Set an audio format
        $audio_format = new \FFMpeg\Format\Audio\Mp3();

        // Extract the audio into a new file as mp3
        $video->save($audio_format, storage_path('audio.mp3'));

        // Set the audio file
        $audio = $this->ffmpeg->open( storage_path('audio.mp3') );

        // Create the waveform
        $waveform = $audio->waveform(640, 120, array('#00FF00'));
        $waveform->save(storage_path('waveform.png'));
    }

    /**
     * 过滤器
     * Filters
     * he output file MUST use the PNG extension.
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function filters($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $video->filters()
              ->resize(new \FFMpeg\Coordinate\Dimension(720, 540), \FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_INSET, true)
              ->framerate(new \FFMpeg\Coordinate\FrameRate(25), 25)
              ->rotate(\FFMpeg\Filters\Video\RotateFilter::ROTATE_180)
              ->pad(new \FFMpeg\Coordinate\Dimension(820, 640))
              ->synchronize();

        $video->save(new \FFMpeg\Format\Video\X264(), storage_path('video_filters5.avi'));
    }

    /**
     * 水印
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function watermark($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $watermarkPath = storage_path('watermark.png');
        $watermarkPath = 'logo.png';
        $video->filters()
              ->watermark($watermarkPath, array(
//                  'position' => 'absolute',
//                  'x' => 1180,
//                  'y' => 620,
                    'position' => 'relative',
                    'bottom' => 150,
                    'right' => 150,
              ))
              ->synchronize();

        $video->save(new \FFMpeg\Format\Video\X264(), storage_path('video_watermark.avi'));
    }

    /**
     * 帧率
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function framerate($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $video->filters()
            //->resize(new \FFMpeg\Coordinate\Dimension(720, 540), \FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_INSET, true)
            ->framerate(new \FFMpeg\Coordinate\FrameRate(5), 10)
            //->rotate(\FFMpeg\Filters\Video\RotateFilter::ROTATE_180)
            //->pad(new \FFMpeg\Coordinate\Dimension(820, 640))
            ->synchronize();

        $video->save(new \FFMpeg\Format\Video\X264(), storage_path('video_framerate4.mp4'));
    }

    /**
     * 裁剪视频大小
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function crop($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $video->filters()
            ->crop(new \FFMpeg\Coordinate\Point("100", 0, true), new \FFMpeg\Coordinate\Dimension(720, 540))
            //->rotate(\FFMpeg\Filters\Video\RotateFilter::ROTATE_180)
            //->pad(new \FFMpeg\Coordinate\Dimension(820, 640))
            ->synchronize();

        $video->save(new \FFMpeg\Format\Video\X264(), storage_path('video_crop3.avi'));
    }

    /**
     * 音频转码
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $audioFile
     */
    public function audioTranscoding($audioFile)
    {
        $audio = $this->ffmpeg->open($audioFile);

        $format = new \FFMpeg\Format\Audio\Flac();
        $format->on('progress', function ($audio, $format, $percentage) {
            echo "$percentage % transcoded";
        });

        $format
            ->setAudioChannels(2)
            ->setAudioKiloBitrate(256);

        $audio->save($format, storage_path('track.flac'));
    }

    /**
     * 音频剪辑
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $audioFile
     */
    public function audioClipping($audioFile)
    {
        $audio = $this->ffmpeg->open($audioFile);

        $audio->filters()->clip(\FFMpeg\Coordinate\TimeCode::fromSeconds(30), \FFMpeg\Coordinate\TimeCode::fromSeconds(15));

        $audio->save((new \FFMpeg\Format\Audio\Mp3()), storage_path('track.mp3'));
    }

    /**
     * 视频生成git
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function Gif($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        $video->gif(\FFMpeg\Coordinate\TimeCode::fromSeconds(10), new \FFMpeg\Coordinate\Dimension(720, 540), 10)
              ->save( storage_path('video.gif'));
    }

    /**
     * 视频生成git
     *
     * https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x?tab=readme-ov-file#generate-a-waveform
     * @param $videoFile
     */
    public function concatenation($videoFile)
    {
        $video = $this->ffmpeg->open($videoFile);

        //相同编码格式
//        $video->concat([storage_path('video/input0.mp4'), storage_path('video/input1.mp4'), storage_path('video/input2.mp4'), storage_path('video/input3.mp4')])
//              ->saveFromSameCodecs(storage_path('video/concatenation6.mp4'), TRUE);

        //不同编码格式
        $videos = [storage_path('video/1.mp4'), storage_path('video/2.mp4'), storage_path('video/3.mp4'), storage_path('video/4.avi')];
//        $cVideos = [];
//        foreach ($videos as $k=>$videof){
//            $videov = $this->ffmpeg->open($videof);
//
//            $videov->filters()
//                ->resize(new \FFMpeg\Coordinate\Dimension(304, 540), \FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_FIT, true)
//                ->framerate(new \FFMpeg\Coordinate\FrameRate(30), 30)
//                ->synchronize();
//
//            $videoFilev = storage_path('video/input'.$k.'.mp4');
//            $videov->save(new \FFMpeg\Format\Video\X264(), $videoFilev);
//            $cVideos[] = $videoFilev;
//        }

        $format = new \FFMpeg\Format\Video\X264();
        $format->setAudioCodec("libmp3lame");
        $video->concat($videos)
              ->saveFromDifferentCodecs($format,storage_path('video/concatenation8.mp4'));
    }
}
